/**
 *  pipe.c - Related routines of pipe.
 *
 *  Copyright (C) 2008-2010 ZhangHu
 *  All rights reserved.
 *  E-MAIL: anmnmnly@gmail.com
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "include/pipe.h"


/**
 * pipeInit - It creates a new pipe.
 * @SendID: ID of task that sends data to pipe.
 * @RevID: ID of task that receives data from pipe.
 * @Size: The size of pipe.
 * @return: The start address of new pipe.
 *
 * @notes:
 */
pipe_t *pipeInit(int32 SendID, int32 RevID, uint32 Size)
{
    pipe_t *pipe = NULL;
    void **ptmp = NULL;

    if(SendID == RevID) {
        return(NULL);
    }

    pipe = (pipe_t*)kmalloc(sizeof(pipe_t));
    if(pipe == NULL) {
        return(NULL);
    }

    ptmp = (void**)kmalloc(Size * sizeof(void*));
    if(ptmp == NULL) {
        kfree(pipe);
        return(NULL);
    }

    pipe->in = ptmp;
    pipe->out = ptmp;
    pipe->start = ptmp;
    pipe->end = &ptmp[Size - 1];
    pipe->send_id = SendID;
    pipe->rev_id = RevID;
    pipe->size = Size;
    pipe->used = 0;
    return(pipe);
}



/**
 * pipeSend - Sending a message to a pipe.
 * @pPipe: Pointer to the pipe.
 * @pMsg: Pointer to the message.
 * @err: It will return rusults or reasons about operation.
 * @return: TURE if successful, otherwise FALSE;
 *
 * @notes:
 */
bool_t pipeSend(pipe_t *pPipe, void *pMsg)
{
    if((pPipe == NULL) || (pMsg == NULL) ||
       (pPipe->start == NULL)) {
        return(FALSE);
    }

    if(current()->id != pPipe->send_id) {
        return(FALSE);
    }

    mac_disable_irq();
    if(pPipe->size == pPipe->used) {
        mac_enable_irq();
        return(FALSE);
    }
    pPipe->used++;
    mac_enable_irq();

    *pPipe->in = pMsg;
    if(pPipe->in++ == pPipe->end) {
        pPipe->in = pPipe->start;
    }

    return(TRUE);
}



/**
 * pipeRev - Retrieve message from pipe.
 * @pPipe: Pointer to the pipe.
 * @err: It will return rusults or reasons of this operation.
 * @return: The message if successful, NULL for failed.
 *
 * @notes:
 */
void *pipeRev(pipe_t *pPipe)
{
    void **pmsg = NULL;

    if((pPipe == NULL) || (pPipe->start == NULL)) {
        return(NULL);
    }

    if(current()->id != pPipe->rev_id) {
        return(NULL);
    }

    mac_disable_irq();
    if(pPipe->used == 0) {
        mac_enable_irq();
        return(NULL);
    }

    pPipe->used--;
    mac_enable_irq();

    pmsg = pPipe->out;
    if(pPipe->out++ == pPipe->end) {
        pPipe->out = pPipe->start;
    }

    return(*pmsg);
}



/**
 * pipeGetUsed - Get the used space.
 * @pPipe: Pointer to the pipe.
 * @return: the number of use space.
 *
 * @notes:
 */
int32 pipeGetUsed(const pipe_t *pPipe)
{
    if((pPipe != NULL) && (pPipe->start != NULL)) {
        return pPipe->used;
    }
    return -1;
}



/**
 * pipeClear - Clear a pipe.
 * @pPipe: Pointer to the pipe.
 * @return:
 *
 * @notes:
 */
void pipeClear(pipe_t *pPipe)
{
    if((pPipe != NULL) && (pPipe->start != NULL)) {
        mac_disable_irq();
        pPipe->used = 0;
        pPipe->in = pPipe->start;
        pPipe->out = pPipe->start;
        mac_enable_irq();
    }
}



/**
 * pipeDel - Delete a pipe.
 * @pPipe: Pointer to the pipe.
 * @return:
 *
 * @notes:
 */
void pipeDel(pipe_t *pPipe)
{
    if((pPipe != NULL) && (pPipe->start != NULL)) {
        kfree(pPipe->start);
    }

    if(pPipe != NULL) {
        kfree(pPipe);
    }
}

